<?php
/**
 * @fileName:DbToMarkdonw.php
 * @project: miaosang
 * @nameSpace:miaosang\yii2\markdown
 * @Class DbToMarkdonw
 * @author : 吐火青蛙
 * @user : 吐火青蛙
 * @date : 2021-09-01 00:12
 * @desc : DbToMarkdonw
 * @Copyright (c)  2021,All Rights Reserved.
 */

namespace miaosang\yii2\markdown;

use miaosang\yii2\bases\Component;
use miaosang\yii2\traits\CreateObject;

class DbToMarkdown extends Component
{
    use CreateObject;

    /**
     * @desc dbComs 存储db组件数组
     * @var
     */

    public $db_coms = [];

    private $markdown_dir;

    private $ignore_databases = ["information_schema", "mysql", "performance_schema", "sys", 'phpmyadmin'];
    public $database_suffix = '/^[\dA-Fa-f]{1,3}$/';
    public $table_suffix = '/^[\dA-Fa-f]{1,3}$/';
    public $log_show = false;

    public function __construct($config = [])
    {
        parent::__construct($config);
        if (!function_exists("exec")) {
            exit("请开启exec 运行函数");
        }

        $this->log("初始化文件生成路径");
        $this->markdown_dir = \yii::$app->runtimePath . DIRECTORY_SEPARATOR . 'dbToMarkdown';
        // 查看README 文件是否存在
        $read_file = $this->markdown_dir . '/README.md';
        if (!file_exists($read_file)) {
            file_put_contents('# ', $read_file);
        }
    }

    public function toMarkDown($summary = '')
    {
        while (!empty($this->db_coms)) {
            $db = array_shift($this->db_coms);
            if (empty($db)) {
                continue;
            }
            $this->log("开始出来数据库" . json_encode($db, JSON_UNESCAPED_UNICODE));
            $db_lists = $db->createCommand("SHOW DATABASES")->queryAll();
            $db_lists = array_column($db_lists, "Database");
            $db_lists = array_diff($db_lists, $this->ignore_databases);
            $db_lists = $this->_format_dataBases($db_lists);
            while (!empty($db_lists)) {
                $db_list = array_shift($db_lists);
                $tmp_db = $db_list;
                if (is_array($tmp_db)) {
                    $tmp_db = $tmp_db[0];
                }
                $dsnArray = explode(";", $db->dsn);

                $dsnArray[1] = "dbname=" . $tmp_db;
                $db->close();
                $db->dsn = implode(";", $dsnArray);
                $db->open();
                $this->log("数据库[" . implode(":", $db_list) . "]处理中");
                $all_tables = $db->getSchema()->getTableNames();
                $all_tables = $this->_format_dataBases($all_tables);

                $table_index = 0;
                array_walk($all_tables, function ($val) use ($db, $dsnArray, &$markStr, &$summary, $tmp_db, &$table_index, $db_list) {
                    $explodeDsn = explode("=", $dsnArray[1]);
                    $baseName = $explodeDsn[1];
                    // 获取表信息
                    $markStr = '';
                    $tableInfo = $db->createCommand("SHOW TABLE STATUS LIKE '" . $val[0] . "'")->queryAll();
                    $indexInfo = $db->createCommand("SHOW INDEX FROM " . $val[0])->queryAll();
                    $columnsInfo = $db->getTableSchema($val[0])->columns;
                    $markStr .= $this->markTable($tableInfo, $columnsInfo, $val);
                    $markStr .= $this->markIndex($tableInfo, $indexInfo);
                    $fileDir = $this->markdown_dir;
                    $tmpDir = $fileDir . '/' . $baseName;
                    if (!is_dir($tmpDir)) {
                        mkdir($tmpDir, "0755", true);
                    }
                    $fileName = $tmpDir . '/' . $tableInfo[0]['Name'] . '.md';
                    $sum_table_name = $tableInfo[0]['Name'];
                    $sum_table_info = $tableInfo[0]['Comment'];

                    if (!$table_index) {
                        $summary .= <<<DATABASES

* $tmp_db
DATABASES;
                    }
                    $summary .= <<<DATABASES

    * [{$sum_table_name}-{$sum_table_info}]($baseName/$sum_table_name.md)
DATABASES;

                    $this->log("    数据表" . $sum_table_name . "生成中。。。。");
                    file_put_contents($fileName, $markStr);
                    $this->log("    数据表" . $sum_table_name . "已完成。。。。");
                    $table_index++;
                });
                $this->log("数据库[" . $tmp_db . "]已完成处理");

            }
            $fileDir = $this->markdown_dir;
            if (!is_dir($fileDir)) {
                mkdir($fileDir, "0755", true);
            }
            $fileName = $fileDir . '/SUMMARY.md';
            file_put_contents($fileName, $summary);
            $this->log("SUMMARY.md 已生成");
            exec("chmod -R 0755 $this->markdown_dir");
            $this->log("chmod -R 0755 调整文件权限");
        }
        return $this;
    }

    /**
     * @name markTable
     * @desc 生成table markdown格式
     * @param $tableInfo
     * @param $arrayColumn
     * @return string
     */
    private function markTable($tableInfo, $arrayColumn, $db_list)
    {
        $tableName = $tableInfo[0]['Name'];
        $tableComment = $tableInfo[0]['Comment'];
        $tableComment = $tableComment == "VIEW" ? '视图' : $tableComment;
        if (count($db_list) == 1) {
            $markDownStr = <<<MARKDOWN

## ${tableName} [${tableComment}]
MARKDOWN;
        } else {
            $db_list = implode(",", $db_list);
            $markDownStr = <<<MARKDOWN
## ${tableName} [${tableComment}]
            
> 其他分表

```
    ${db_list}
```
MARKDOWN;
        }
        $markDownStr .= <<<MARKDOWN

### 表结构

|字段|类型|长度|是否必填|是否为主键|说明|
|:----|:----|:----|:----|:----|:----|
MARKDOWN;
        foreach ($arrayColumn as $ckey => $cavl) {
            $allowNull = $cavl->allowNull ? 'N' : 'Y';
            $isPrimaryKey = $cavl->isPrimaryKey ? 'Y' : 'N';
            $size = $cavl->size ? $cavl->size : '不限';
            $markDownStr .= <<<MARKDOWN

|$cavl->name|$cavl->type|$size|$allowNull|$isPrimaryKey|$cavl->comment|
MARKDOWN;

        }
        return $markDownStr;
    }

    /**
     * @name markIndex
     * @desc 生成索引markdown结构
     * @param $tableInfo
     * @param $indexInfo
     * @return mixed
     */
    private function markIndex($tableInfo, $indexInfo)
    {
        $tableName = $tableInfo[0]['Name'];
        $indexInfo = $this->getTableIndex($indexInfo);
        $markDownStr = <<<MARKDOWN


### 索引

|索引名称|是否唯一索引|是否为主键|字段顺序|说明|
|:----|:----|:----|:----|:----|
MARKDOWN;

        foreach ($indexInfo as $ckey => $cavl) {
            $name = $cavl['name'];
            $isUnique = $cavl['isUnique'] ? 'Y' : 'N';
            $isPrimary = $cavl['isPrimary'] ? 'Y' : 'N';
            $columnNames = join('+', $cavl['val']);
            $comment = $cavl['comment'];
            if ($ckey == 'PRIMARY') {
                $comment = "主键";
            }
            $markDownStr .= <<<MARKDOWN

|$name|$isUnique|$isPrimary|$columnNames|$comment|
MARKDOWN;

        }
        return $markDownStr;
    }

    /**
     * @name getTableIndex
     * @desc 格式化索引
     * @param $indexTmpColumn
     * @return array
     */
    protected function getTableIndex($indexTmpColumn)
    {
        $indexColumn = [];
        foreach ($indexTmpColumn as $indexVal) {
            $indexColumn[$indexVal['Key_name']]['isUnique'] = $indexVal['Non_unique'] ? false : true;
            $indexColumn[$indexVal['Key_name']]['isPrimary'] = $indexVal['Key_name'] == "PRIMARY" ? true : false;
            $indexColumn[$indexVal['Key_name']]['name'] = $indexVal['Key_name'];
            $indexColumn[$indexVal['Key_name']]['comment'] = $indexVal['Index_comment'];
            if (!key_exists('val', $indexColumn[$indexVal['Key_name']])) {
                $indexColumn[$indexVal['Key_name']]['val'] = [];
            }
            array_push($indexColumn[$indexVal['Key_name']]['val'], $indexVal['Column_name']);
        }
        return $indexColumn;
    }

    /**
     * 格式化数组
     * @param  array  $dbTables
     * @return array
     */
    private function _format_dataBases(array $dbTables): array
    {
        if (empty($dbTables)) {
            return [];
        }
        $tmp_db_array = [];
        while (!empty($dbTables)) {
            $tmp_db_tables = array_shift($dbTables);
            $suffix = explode('_', $tmp_db_tables);
            $end_suffix = array_pop($suffix);
            $array_key = $tmp_db_tables;
            if (preg_match($this->database_suffix, $end_suffix)) {
                $array_key = implode("_", $suffix);
            }
            if (empty($tmp_db_array)) {
                $tmp_db_array[$array_key][] = $tmp_db_tables;
            } else {
                $tmp_db_array[$array_key][] = $tmp_db_tables;
            }
        }
        return $tmp_db_array;
    }

    /**
     * 运行脚本
     */
    public function run()
    {
        $str = exec("which gitbook");
        if ($str == '' || empty($str)) {
            exit("请安装gitbook 运行环境");
        }
        $this->log("开始运行 gitbook serve");
        $this->log(exec("cd $this->markdown_dir && gitbook serve"));
    }

    /**
     * 自动生成html
     */
    public function build()
    {
        $str = exec("which gitbook");
        if ($str == '' || empty($str)) {
            exit("请安装gitbook 运行环境");
        }
        $this->log("开始运行 gitbook build");
        exec("cd $this->markdown_dir && gitbook build");
    }

    /**
     * 数据库生成gitbook 格式
     */
    public function toGitBook()
    {
        $summary = <<<SUMMARY
# 目录

* [说明简介](README.md)
SUMMARY;
        $summaryArray = $this->toMarkDown(true, $summary);
        return $this;
    }

    /**
     * @desc PUSH Db
     * @param $db
     * @return $this
     * @throws \Exception
     */
    public function push($db): DbToMarkdown
    {
        $this->log("创建db组件" . json_encode($db, JSON_UNESCAPED_UNICODE));
        array_push($this->db_coms, $this->createObject($db));
        return $this;
    }

    /**
     *  输出调试日志
     * @param $msg
     */
    private function log($msg)
    {
        if ($this->log_show == true) {
            echo $msg . PHP_EOL;
        }
    }

}